关于缓存:WPF TabControl 您所在的位置:网站首页 wpf tabcontrol切换菜单界面卡 关于缓存:WPF TabControl

关于缓存:WPF TabControl

2024-03-25 11:49| 来源: 网络整理| 查看: 265

在WPF选项卡控件中更改选项卡时,是否有办法防止选项卡卸载/重新加载? 还是如果不可能,是否有建议的方法来缓存选项卡的内容,这样就不必每次更改选项卡时都重新生成它们?

例如,一个选项卡的UI可以完全自定义并存储在数据库中。 当用户选择要处理的对象时,自定义布局中的项目将填充有该对象的数据。 用户预期初始加载或检索数据时会有轻微的延迟,但在选项卡之间来回切换时却不会,并且在更改选项卡时的延迟非常明显。

相关讨论 我不认为每当选项卡控件中的选定项目更改时,都会卸载/重新加载TabItems。 我不确定,但是也许需要更改TabControl的SelectionChanged逻辑,以便它不会每次都重新查询数据库? 每当我更改标签时(我使用MVVM设计模式),都会运行DataTemplates的Load / Unloaded事件 那么,在您的应用程序中,每当选定的选项卡更改时,它都会触发与数据库的连接以检索对象数据吗? 是的,我想缓存选项卡,以便它不必重新构建它,或者执行某种变通办法来防止它在选项卡更改时卸载/重新加载内容。

我在这里找到了解决方法:https://web.archive.org/web/20120429044747/http://eric.burke.name/dotnetmania/2009/04/26/22.09.28

Edit: This is the corrected link: http://web.archive.org/web/20110825185059/http://eric.burke.name/dotnetmania/2009/04/26/22.09.28

它基本上存储了选项卡的ContentPresenter,并在切换选项卡而不是重新绘制时加载了该选项。由于这是一个删除/添加操作,仍然在拖动/拖放选项卡时造成延迟,但是我也做了一些修改,使它也消失了(以较低的调度程序优先级运行删除代码,而不是添加代码,因此添加操作有机会取消删除操作并使用旧的ContentPresenter而不是绘制新的)

编辑:上面的链接似乎不再起作用,因此我将在此处粘贴代码的副本。对其进行了一些修改,以允许拖放,但仍应以相同的方式工作。

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261using System; using System.Windows; using System.Windows.Threading; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Collections.Specialized; // Extended TabControl which saves the displayed item so you don't get the performance hit of // unloading and reloading the VisualTree when switching tabs // Obtained from http://eric.burke.name/dotnetmania/2009/04/26/22.09.28 // and made a some modifications so it reuses a TabItem's ContentPresenter when doing drag/drop operations [TemplatePart(Name ="PART_ItemsHolder", Type = typeof(Panel))] public class TabControlEx : System.Windows.Controls.TabControl {     // Holds all items, but only marks the current tab's item as visible     private Panel _itemsHolder = null;     // Temporaily holds deleted item in case this was a drag/drop operation     private object _deletedObject = null;     public TabControlEx()         : base()     {         // this is necessary so that we get the initial databound selected item         this.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;     }     ///     /// if containers are done, generate the selected item     ///     ///     ///     void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)     {         if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)         {             this.ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged;             UpdateSelectedItem();         }     }     ///     /// get the ItemsHolder and generate any children     ///     public override void OnApplyTemplate()     {         base.OnApplyTemplate();         _itemsHolder = GetTemplateChild("PART_ItemsHolder") as Panel;         UpdateSelectedItem();     }     ///     /// when the items change we remove any generated panel children and add any new ones as necessary     ///     ///     protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)     {         base.OnItemsChanged(e);         if (_itemsHolder == null)         {             return;         }         switch (e.Action)         {             case NotifyCollectionChangedAction.Reset:                 _itemsHolder.Children.Clear();                 if (base.Items.Count > 0)                 {                     base.SelectedItem = base.Items[0];                     UpdateSelectedItem();                 }                 break;             case NotifyCollectionChangedAction.Add:             case NotifyCollectionChangedAction.Remove:                 // Search for recently deleted items caused by a Drag/Drop operation                 if (e.NewItems != null && _deletedObject != null)                 {                     foreach (var item in e.NewItems)                     {                         if (_deletedObject == item)                         {                             // If the new item is the same as the recently deleted one (i.e. a drag/drop event)                             // then cancel the deletion and reuse the ContentPresenter so it doesn't have to be                             // redrawn. We do need to link the presenter to the new item though (using the Tag)                             ContentPresenter cp = FindChildContentPresenter(_deletedObject);                             if (cp != null)                             {                                 int index = _itemsHolder.Children.IndexOf(cp);                                 (_itemsHolder.Children[index] as ContentPresenter).Tag =                                     (item is TabItem) ? item : (this.ItemContainerGenerator.ContainerFromItem(item));                             }                             _deletedObject = null;                         }                     }                 }                 if (e.OldItems != null)                 {                     foreach (var item in e.OldItems)                     {                         _deletedObject = item;                         // We want to run this at a slightly later priority in case this                         // is a drag/drop operation so that we can reuse the template                         this.Dispatcher.BeginInvoke(DispatcherPriority.DataBind,                             new Action(delegate()                         {                             if (_deletedObject != null)                             {                                 ContentPresenter cp = FindChildContentPresenter(_deletedObject);                                 if (cp != null)                                 {                                     this._itemsHolder.Children.Remove(cp);                                 }                             }                         }                         ));                     }                 }                 UpdateSelectedItem();                 break;             case NotifyCollectionChangedAction.Replace:                 throw new NotImplementedException("Replace not implemented yet");         }     }     ///     /// update the visible child in the ItemsHolder     ///     ///     protected override void OnSelectionChanged(SelectionChangedEventArgs e)     {         base.OnSelectionChanged(e);         UpdateSelectedItem();     }     ///     /// generate a ContentPresenter for the selected item     ///     void UpdateSelectedItem()     {         if (_itemsHolder == null)         {             return;         }         // generate a ContentPresenter if necessary         TabItem item = GetSelectedTabItem();         if (item != null)         {             CreateChildContentPresenter(item);         }         // show the right child         foreach (ContentPresenter child in _itemsHolder.Children)         {             child.Visibility = ((child.Tag as TabItem).IsSelected) ? Visibility.Visible : Visibility.Collapsed;         }     }     ///     /// create the child ContentPresenter for the given item (could be data or a TabItem)     ///     ///     ///     ContentPresenter CreateChildContentPresenter(object item)     {         if (item == null)         {             return null;         }         ContentPresenter cp = FindChildContentPresenter(item);         if (cp != null)         {             return cp;         }         // the actual child to be added.  cp.Tag is a reference to the TabItem         cp = new ContentPresenter();         cp.Content = (item is TabItem) ? (item as TabItem).Content : item;         cp.ContentTemplate = this.SelectedContentTemplate;         cp.ContentTemplateSelector = this.SelectedContentTemplateSelector;         cp.ContentStringFormat = this.SelectedContentStringFormat;         cp.Visibility = Visibility.Collapsed;         cp.Tag = (item is TabItem) ? item : (this.ItemContainerGenerator.ContainerFromItem(item));         _itemsHolder.Children.Add(cp);         return cp;     }     ///     /// Find the CP for the given object.  data could be a TabItem or a piece of data     ///     ///     ///     ContentPresenter FindChildContentPresenter(object data)     {         if (data is TabItem)         {             data = (data as TabItem).Content;         }         if (data == null)         {             return null;         }         if (_itemsHolder == null)         {             return null;         }         foreach (ContentPresenter cp in _itemsHolder.Children)         {             if (cp.Content == data)             {                 return cp;             }         }         return null;     }     ///     /// copied from TabControl; wish it were protected in that class instead of private     ///     ///     protected TabItem GetSelectedTabItem()     {         object selectedItem = base.SelectedItem;         if (selectedItem == null)         {             return null;         }         if (_deletedObject == selectedItem)         {         }         TabItem item = selectedItem as TabItem;         if (item == null)         {             item = base.ItemContainerGenerator.ContainerFromIndex(base.SelectedIndex) as TabItem;         }         return item;     } }

相关讨论 我认为StackOverflow只是错误地解析了链接markdown。如果您复制/粘贴整个URL(或不使用markdown),则此方法有效。 web.archive.org/web/20110825185059/http://eric.burke.name/ 我正在尝试使用您的解决方案,因为它似乎可以解决我面临的确切问题。但是,我不知道到底该如何完成工作。例如,变量_itemsHolder始终为null。该控件位于我的XAML中,并且所有内容都能正确显示,但是XAML中是否有我需要弥补的特殊参考? @DonBoitnott它在什么时候为空?您说它可以正常工作,因此必须在某个时候填充它。 好吧,"正确地"只是在一切都正确显示的意义上。它通过调用GetTemplateChild在OnApplyTemplate中实例化,在此点及之后的位置为null。值得一提的是,我是WPF的新手,我不知道" PART_ItemsHolder"的用途是什么。 @DonBoitnott如果它在OnApplyTemplate中设置,那么它应该在那里。您确定要查看对象模型的相同实例吗?您在什么时候放置一个断点并看到它为空?如果您对此有疑问,最好提出一个新问题,这样我们就可以看到相关的代码。另外,我相信" PART_ItemsHolder"是默认WPF TabControl模板的一部分 @Rachel我的问题的答案最终是此答案中定义的ControlTemplate。

除此之外,我遇到了一个类似的问题,并设法通过缓存一个用户控件来解决该问题,该控件代表了后面代码中的选项卡项目的内容。

在我的项目中,我有一个绑定到集合(MVVM)的选项卡控件。但是,第一个选项卡是概述,在列表视图中显示了所有其他选项卡的摘要。我遇到的问题是,每当用户将其选择从项目选项卡移动到概述选项卡时,都会使用所有摘要数据重新绘制概述,这可能需要10到15秒的时间,具体取决于集合中项目的数量。 (请注意,它们并不是从数据库或其他任何东西重新加载实际数据,纯粹是花费时间的摘要视图的图)。

我想要的是摘要视图的这种加载仅在第一次加载数据上下文时发生一次,并且随后在选项卡之间进行的任何切换都是瞬时的。

解:

涉及的课程: MainWindow.xaml-包含选项卡控件的主页。 MainWindow.xaml.cs-上面的代码在后面。 MainWindowViewModel.cs-上面视图的视图模型包含集合。 Overview.xaml-绘制概述选项卡项目内容的用户控件。 OverviewViewModel.cs-上面视图的视图模型。

脚步:

用一个名为" OverviewPlaceholder"的空白用户控件替换" MainWindow.xaml"中绘制"概述"选项卡项的数据模板

在" MainWindowViewModel.cs"中公开引用" OverviewViewModel"

在" MainWindow.xaml.cs"中添加对"概述"的静态引用

将事件处理程序添加到用户控件" OverviewPlaceholder"的已加载事件中,在此方法内,仅当静态引用为null时,才将静态引用实例化为" Overview",将此引用的数据上下文设置为当前数据上下文中的" OverviewViewModel"引用(即是" MainWindowViewModel"),并将占位符的内容设置为对"概述"的静态引用。

现在,概述页面仅绘制一次,因为每次加载概述页面时(即用户单击"概述"选项卡),它会将已呈现的静态用户控件放回到页面上。

我有一个非常简单的解决方案,可以避免在更改标签时重新加载标签, 在tabItem中使用contentPresenter而不是content属性。

例如(以MVVM样式)

更换

1     

通过

123                           

相关讨论 为什么这个答案被赞成? ContentPresenter将在选项卡切换(包括其内容)上卸载。这什么也没做。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有